<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<script>

        const data = {
            count: 0,
            visible: true,
            userList: [
                { name: 'zhangsan', age: 18, id: 0 },
                { name: 'lisi', age: 28, id: 1 },
                { name: 'lisi', age: 28, id: 1 },
                { name: 'lisi', age: 28, id: 1 },
            ],
        };

        // 虚拟DOM（vDom）
        function getVDom (data) {
            return preprocessor({
                tag: 'div',
                attrs: {
                    class: 'list',
                },
                events: {
                    click () {
                        // data.count++;
                        data.visible = !data.visible;
                        // console.log(data.count);
                    },
                },
                content: data.count,
                children: [
                    {
                        tag: 'div',
                        content: 'user.name',
                        directives: {
                            // 'v-if': data.visible,
                            // 'v-show': data.visible,
                            'v-for': '(user, index) in userList',
                        },
                        props: {
                            key: 'user.id',
                        },
                    },
                ],
            }, data)
        }

        function makeObsevableData (data) {
            const record = { ...data };
            for (const key in data) {
                Object.defineProperty(data, key, {
                    set (newValue) {
                        record[key] = newValue;
                        document.body.innerHTML = '';
                        document.body.appendChild(render(getVDom(data)));
                    },
                    get () {
                        return record[key];
                    },
                });
            }
        }

        function render (vDom) {

            if (vDom.directives?.['v-if'] === false) {
                return null;
            }

            const el = document.createElement(vDom.tag);

            for (const key in vDom.attrs || {}) {
                el.setAttribute(key, vDom.attrs[key]);
            }

            for (const eventType in vDom.events || {}) {
                el.addEventListener(eventType, vDom.events[eventType]);
            }

            el.innerText = vDom.content !== undefined ? vDom.content : '';

            if (vDom.directives?.['v-show'] === false) {
                el.setAttribute('style', (vDom.attrs?.style || '') + 'display: none;');
            }

            for (const childVDom of vDom.children || []) {
                if (Array.isArray(childVDom)) {
                    for (const itemVDom of childVDom) {
                        const childEl = render(itemVDom);
                        childEl && el.appendChild(childEl);
                    }
                } else {
                    const childEl = render(childVDom);
                    childEl && el.appendChild(childEl);
                }
            }
            return el;
        }

        function preprocessor (vDom, data) {
            if (vDom.directives?.['v-for']) {
                const arrKey = vDom.directives['v-for'].split(' ').slice(-1)[0];
                const arr = data[arrKey]; // userList
                const currentVDom = JSON.parse(JSON.stringify(vDom));
                currentVDom.directives['v-for'] = undefined;
                return arr.map((item, i) => {
                    return preprocessor(currentVDom, data);
                });
            }
            if (!vDom.children) {
                return vDom;
            }
            for (let i = 0; i < vDom.children.length; i++) {
                vDom.children[i] = preprocessor(vDom.children[i], data)
            }
            return vDom;
        }

        makeObsevableData(data);
        const rootNode = render(getVDom(data));
        rootNode && document.body.appendChild(rootNode);

</script>
</body>
</html>